/**
 * 
 */
package com.manolo.server;

import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import com.google.gson.Gson;
import com.manolo.server.connection.Server;
import com.manolo.server.dispatcher.ProcessCommunicationDispatcher;
import com.manolo.server.dispatcher.ServerCommunicationDispatcher;
import com.manolo.event.EventType;
import com.manolo.server.entity.Game;
import com.manolo.event.entity.Message;
import com.manolo.server.entity.Player;
import com.manolo.event.HandCardEvent;
import com.manolo.event.ChatEvent;
import com.manolo.event.PlayerListEvent;
import com.manolo.server.event.ProcessEvent;
import com.manolo.server.event.ServerDataEvent;
import com.manolo.event.SystemEvent;
import com.manolo.server.listener.ServerCommunicationListener;

/**
 * @author meyer
 *
 */
public abstract class GameGenericCommunication implements Runnable, ServerCommunicationListener {
	
	protected static final Integer NUMBER_PLAYER_TO_START_GAME = 5;
	protected List queue = new LinkedList();
	protected Map<SocketChannel, Player> mapPlayer = new HashMap<SocketChannel, Player>();
	protected LinkedList<Game> gameList = new LinkedList<Game>();
	private Integer lastId = 0;
	private Server server;
	private Gson gson;

	public int getNumberPlayer() {
		return mapPlayer.size();
	}

	public void setServer(Server server) {
		this.server = server;
	}

	public GameGenericCommunication() {
		// Receive event to dispatch
		gson = new Gson();
		ServerCommunicationDispatcher.getInstance()
				.addServerCommunicationListener(this);
	}

	public GameGenericCommunication(Server server) {
		super();
		this.server = server;
	}

	public void processData(SocketChannel socket, byte[] data, int count) {
		byte[] dataCopy = new byte[count];
		Message msg;
		System.arraycopy(data, 0, dataCopy, 0, count);

		String in = new String(dataCopy);
		System.out.println("teste");
		try {
			msg = gson.fromJson(in, Message.class);

			switch (msg.getEventType()) {
			case SYSTEM:
				SystemEvent system = gson.fromJson(msg.getEvent(),
						SystemEvent.class);

				if (msg.getMethod().equals("defineName")) {
					this.defineName(system, socket);
				}
				if(msg.getMethod().equals("getPlayerList")){
					this.getPlayerList(socket);
				}
				break;
			case CHAT:
				ChatEvent chat = gson.fromJson(msg.getEvent(), ChatEvent.class);

				if (msg.getMethod().equals("globalChat")) {
					this.globalChat(chat, channelOwner(socket));
				}

				break;
//			case CARD:
//				HandCardEvent cardEvent = gson.fromJson(msg.getEvent(), HandCardEvent.class);
//
//				if (msg.getMethod().equals("globalChat")) {
//					this.globalChat(cardEvent, channelOwner(socket));
//				}
//
//				break;

			default:
				break;
			}
		} catch (Exception e) {
			System.out.println("Invalid Server Message: [" + in + "]");
			// TODO: handle exception
		}

		/*
		 * synchronized (queue) { Iterator<Entry<SocketChannel, Player>>
		 * itMapPlayer = mapPlayer.entrySet().iterator();
		 * 
		 * while(itMapPlayer.hasNext()){ Entry<SocketChannel, Player> entry =
		 * itMapPlayer.next(); Player pl = entry.getValue(); SocketChannel
		 * chanel = entry.getKey(); queue.add(new
		 * ServerDataEvent(pl.getServer(),chanel,
		 * ("\n vai trabalhar porra ").getBytes())); queue.notify();
		 * 
		 * }
		 * 
		 * }
		 */

	}

	private void globalChat(ChatEvent evt, Player player) {
		List msgList = new LinkedList();
		Iterator<Entry<SocketChannel, Player>> itMapPlayer = mapPlayer
				.entrySet().iterator();
		Message msg;
		
		if(evt.getText().equals("/startgame"))
		{
			this.gameStart();
		}
		else
		{
			evt.setText(player.getName() + ": " + evt.getText());
	
			while (itMapPlayer.hasNext()) {
				Entry<SocketChannel, Player> entry = itMapPlayer.next();
				Player pl = entry.getValue();
				SocketChannel channel = entry.getKey();
				msg = new Message();
				msg.setEventType(EventType.CHAT);
				msg.setMethod("globalChat");
				msg.setEvent(gson.toJson(evt));
				addMessageToQueue(msg, channel);
			}
		}
	}
	
	private void getPlayerList(SocketChannel channel)
	{
		LinkedList<String> playerList = new LinkedList<String>();
		Iterator<Entry<SocketChannel, Player>> itMapPlayer;
		
		Message msg = new Message();
		
		itMapPlayer =  mapPlayer.entrySet().iterator();
		while(itMapPlayer.hasNext()){
			Entry<SocketChannel, Player> entry = itMapPlayer.next();
			Player pl = entry.getValue();
			
			playerList.add(pl.getName());
		}
		
		PlayerListEvent evt = new PlayerListEvent();
		evt.setPlayerName("");
		evt.setPlayerList(playerList);
		msg.setEventType(EventType.SYSTEM);
		msg.setMethod("setPlayerList");
		msg.setEvent(gson.toJson(evt));
		
		addMessageToQueue(msg, channel);
	}
	
	private void notifyPlayerJoined(String playerName, SocketChannel channel)
	{
		Message msg = new Message();
		Iterator<Entry<SocketChannel, Player>> itMapPlayer;
		
		PlayerListEvent evt = new PlayerListEvent();
		evt.setPlayerName(playerName);
		msg.setEvent(gson.toJson(evt));
		msg.setEventType(EventType.SYSTEM);
		msg.setMethod("playerJoined");
		msg.setEvent(gson.toJson(evt));
		
		itMapPlayer =  mapPlayer.entrySet().iterator();
		while(itMapPlayer.hasNext()){
			Entry<SocketChannel, Player> entry = itMapPlayer.next();
			Player pl = entry.getValue();
			SocketChannel ch = entry.getKey();
			if(channel.equals(ch))
				continue;
			
			addMessageToQueue(msg, ch);
		}
		
	}
	
	private void notifyPlayerLeft(String playerName, SocketChannel channel)
	{
		Message msg = new Message();
		Iterator<Entry<SocketChannel, Player>> itMapPlayer;
		
		PlayerListEvent evt = new PlayerListEvent();
		evt.setPlayerName(playerName);
		msg.setEvent(gson.toJson(evt));
		msg.setEventType(EventType.SYSTEM);
		msg.setMethod("playerLeft");
		msg.setEvent(gson.toJson(evt));
		
		itMapPlayer =  mapPlayer.entrySet().iterator();
		while(itMapPlayer.hasNext()){
			Entry<SocketChannel, Player> entry = itMapPlayer.next();
			Player pl = entry.getValue();
			SocketChannel ch = entry.getKey();
			if(channel.equals(ch))
				continue;
			
			addMessageToQueue(msg, ch);
		}
		
	}
	
	/**
	 * Send cards for all player
	 */
	protected void sendCardsAllPlayer() {
		Gson gson = new Gson();
		List msgList = new LinkedList();
		Iterator<Entry<SocketChannel, Player>> itMapPlayer = mapPlayer
				.entrySet().iterator();
		Message msg;
		
		while (itMapPlayer.hasNext()) {
			Entry<SocketChannel, Player> entry = itMapPlayer.next();
			Player pl = entry.getValue();
			SocketChannel channel = entry.getKey();
			HandCardEvent cardEvent = new HandCardEvent();
			cardEvent.getCards().addAll(pl.getCards());
			msg = new Message();
			msg.setEventType(EventType.CARD);
			msg.setMethod("cardEvent");
			msg.setEvent(gson.toJson(cardEvent));
			System.out.println(gson.toJson(msg));
			addMessageToQueue(msg, channel);
		}
	}

	private void defineName(SystemEvent evt, SocketChannel socket) {
		Message msg = new Message();
		if (evt.getName().equals("")) {
			System.out.println(channelOwner(socket).getName()
					+ " declined to change your name to " + evt.getName());
			msg.setEventType(EventType.SYSTEM);
			msg.setMethod("nameDeclined");
			msg.setEvent(gson.toJson(evt));
		} else {
			System.out.println(channelOwner(socket).getName()
					+ " accepted to changed the name to " + evt.getName());
			channelOwner(socket).setName(evt.getName());
			msg.setEventType(EventType.SYSTEM);
			msg.setMethod("nameAccepted");
			msg.setEvent(gson.toJson(evt));
			notifyPlayerJoined(evt.getName(), socket);
			
		}
		addMessageToQueue(msg, socket);
		
		
	}

	public abstract void gameStart();
	
	protected void addMessageToQueue(Message msg, SocketChannel socket) {
		synchronized (queue) {
			ProcessEvent event = new ProcessEvent(socket, gson.toJson(msg).getBytes());
			queue.add(event);
			queue.notify();
		}
	}

	public void notifyPlayerIsWriting(SocketChannel channel) {
		System.out.println(channelOwner(channel).getName() + " - isWriting...");
	}

	public void notifyPlayerIsReading(SocketChannel channel) {
		System.out.println(channelOwner(channel).getName() + " - isReading...");
	}

	public Player channelOwner(SocketChannel channel) {
		return mapPlayer.get(channel);
	}

	public void addPlayer(SocketChannel channel) {

		Player p = new Player();
		synchronized (lastId) {
			p.setId(lastId.intValue());
			lastId += 1;
		}
		p.setName("New Player " + p.getId());
		mapPlayer.put(channel, p);
		System.out.println(p.getName() + " connected to the server");

	}

	public void removePlayer(SocketChannel channel) {
		System.out.println(channelOwner(channel).getName()
				+ " has left the server.");
		notifyPlayerLeft(channelOwner(channel).getName(), channel);
		mapPlayer.remove(channel);
	}
	
	protected void refuseConnetion(SocketChannel channel){
		ProcessEvent processEvent = new ProcessEvent();
		processEvent.setSocket(channel);
//		ProcessCommunicationDispatcher.getInstance().dispatchSendeMessage(processEvent);
		ProcessCommunicationDispatcher.getInstance().dispatchCloseConnection(processEvent);		
	}

}
